package com.jichangxiu.framework.utils;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.jichangxiu.common.constant.Constants;
import com.jichangxiu.common.constant.RedisConstants;
import com.jichangxiu.common.enums.LimitType;
import com.jichangxiu.common.utils.IpUtils;
import com.jichangxiu.common.utils.ServletUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.script.RedisScript;

import java.lang.reflect.Method;
import java.util.Collections;

@Slf4j
public class LimitUtils {

    private static final RedisScript<Long> limitScript = SpringUtil.getBean("limitScript");

    public static void setLimitRequest(ProceedingJoinPoint proceedingJoinPoint, int second, int count, LimitType limitType) {
        log.debug("【LimitUtils】请求【" + ServletUtils.getRequestUri() + "】开启了请求访问限流");
        requestRateLimiter(proceedingJoinPoint, second, count, limitType);
    }

    private static void requestRateLimiter(ProceedingJoinPoint proceedingJoinPoint, int second, int count, LimitType limitType) {
        String limitKey = getLimitKey(proceedingJoinPoint, limitType);
        Long number = RedisUtils.execute(limitScript, Collections.singletonList(limitKey), count, second);
        if (ObjectUtil.isNull(number) || number.intValue() > count)
            throw new RuntimeException("【LimitUtils】访问过于频繁，请稍候再试");
        log.debug("【LimitUtils】请求【" + ServletUtils.getRequestUri() + "】限制次数【" + count + "】当前请求次数【" + number + "】缓存【" + limitKey + "】");
    }

    private static String getLimitKey(ProceedingJoinPoint proceedingJoinPoint, LimitType limitType) {
        StringBuilder stringBuffer = new StringBuilder();
        if (limitType.equals(LimitType.IP))
            stringBuffer.append(RedisConstants.createRequestLimitKey(Constants.JCX_PROPERTIES.getProjectName(), IpUtils.getClientIp())).append("-");
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        Method method = signature.getMethod();
        Class<?> targetClass = method.getDeclaringClass();
        stringBuffer.append(targetClass.getName()).append("-").append(method.getName());
        return stringBuffer.toString();
    }

}
